import Class from "../tools/Class";
/**
 * IOC容器，用来保存项目中自动加载的所有模块，相当于一个大的缓存模块库
 * IOC的功能很简单
 *   添加模块
 *   删除模块
 *   依赖注入
 */
if(!global.__CGEM?.IOC){
    // 如果ioc容器不存在就创建一个
    global.__CGEM = {IOC:Object.create(null)}
}

interface ioc{
    filepath:string; // 模块的文件路径
    metadata:any;     // 保存加载到的元数据
    instance:any;     // 保存的实例化对象
    structureDependsOn:Array<string>; // 构造依赖项
    dependencyInjection:Array<{id:string,variableName:string}>;  // 依赖注入
    loaded:boolean;  // 是否加载完成
}
export default class Ioc {
    private static IOC:ioc = global.__CGEM.IOC; 
    /**
     * 添加模块并且进行依赖注入
     * @Autowired
     * @param id
     * @param module 
     */
    static add(id:string,option:string,module?:any):void;
    static add(id:string,option:{
        path:string,
        structureDependsOn:Array<string>
    },module?:any):void;
    static add(id:string,option:any,module?:any):void{
        let filepath:string,structureDependsOn:Array<string>,dependencyInjection:any[];
        if(typeof option === 'object'){
            filepath = option.path;
            structureDependsOn = option.structureDependsOn || [];
            dependencyInjection = option.dependencyInjection || [];
        }else{
            filepath = option;
            structureDependsOn = [];
            dependencyInjection = []
        }

        // 查看有没有构造器注入
        // 没有构造器，直接进行添加
        this.IOC[id] = {
            // 文件路径
            filepath:filepath,
            // 导入的元数据
            metadata:module || loader(filepath,true),
            // 实例化数据
            instance:{},
            // 构造注入
            structureDependsOn,
            // 依赖注入
            dependencyInjection,
            // 是否完成加载
            loader:false
        }
        // 开始判断是否是异步加载以及是否是一个类
        if(this.IOC[id].metadata instanceof Promise){
            // 这里需要进行异步处理
            process.nextTick(async ()=>{
                this.IOC[id].metadata = await this.IOC[id].metadata;
                if(Class.isClass(this.IOC[id].metadata)){
                    // 这是一个类 创建实例对象
                    this.IOC[id].instance = this.instantiateClass(this.IOC[id].metadata,structureDependsOn);
                }else{
                    this.IOC[id].instance = this.IOC[id].metadata;
                }
                // 注入依赖项
                this.dependent(this.IOC[id].instance,dependencyInjection)
                // 加载成功
                this.IOC[id].loader = true;
            })
        }else if(Class.isClass(this.IOC[id].metadata)){
            // 这是一个类 创建实例化对象
            this.IOC[id].instance = this.instantiateClass(this.IOC[id].metadata,structureDependsOn);
            // 开始进行依赖注入 遍历依赖
            this.dependent(this.IOC[id].instance,dependencyInjection)
            // 加载成功
            this.IOC[id].loader = true;
        }else{
            this.IOC[id].instance = this.IOC[id].metadata;
            this.dependent(this.IOC[id].instance,dependencyInjection)
            this.IOC[id].loader = true;
        }
    }

    /**
     * 获取模块
     * @param id 
     */
    static get(id:string):ioc{
        return this.IOC[id]
    }

    /**
     * 删除模块
     * @param id 
     */
    static delete(id:string):boolean{
       return delete this.IOC[id]
    }

    /**
     * 忽略缓存，创建新的实例
     * @param id 
     */
    static create(id:string){
        let tar = this.get(id);
        // 1.重新实例化模块
        this.add(id,{
            path:tar.filepath,
            structureDependsOn:tar.structureDependsOn
        },tar.metadata)
        return this.get(id)        
    }

    /**
     * 获取实例对象
     * @Param id
     */
    static getInstance(id:string){
        return this.IOC[id]?.instance;
    }

    /**
     * 获取类
     * @param id 
     */
    static getClass(id:string){
        return this.IOC[id]?.metadata;
    }

    /**
     * 注入依赖
     */
    static dependent(target:any,dependencyInjection:Array<{id:string,variableName:string}>){
        dependencyInjection.forEach(e=>{
            // 开始注入
            if(target["set"+e.variableName.trim().toLowerCase().replace(e.variableName[0], e.variableName[0].toUpperCase())]){
                //存在设置的这个方法
                target["set"+e.variableName.trim().toLowerCase().replace(e.variableName[0], e.variableName[0].toUpperCase())](this.get(e.id))
            }else if(target["set"+e.variableName]){
                target["set"+e.variableName](this.get(e.id))
            }else{
                console.log("依赖注入失败")
            }
        })
    }

    /**
     * 创建实例化对象
     * @param clas 
     * @param structureDependsOn 
     * @returns 
     */
    static instantiateClass(clas:any,structureDependsOn:string[]){
        let cla = new clas(...structureDependsOn.map((e:string)=>this.get(e)))
        Object.keys((e:string)=>cla[e] = clas[e])
        return cla;
    }
}

if(module) module.exports = Ioc;
